;DSKPAK.MAC;3 18-Feb-81 22:16:38, Edit by PETERS ;FIX DERROR TO NOT CLOBBER STACK - WE NOW "JSP A,DERROR" LIKE DSKERR ;DSKPAK.MAC;2 17-Feb-81 21:25:08, Edit by PETERS ;REMOVE TEST FOR NOT READY STATUS AT DOOP ;DSK:DSKPAK.CDC;5 28-Jul-80 16:19:51, Edit by FRENCH ;MAKE RETRY 1000. TIMES AGAIN ;DSK:DSKPAK.CDC;4 23-Jul-80 15:21:52, Edit by FRENCH ;MORE BAT QUEUEING STUFF, ALLOW JSYS INTERFACE ETC. (PIOFF LOCK BATENQ) ;DSK:DSKPAK.CDC;26 20-Jun-80 12:18:43, Edit by FRENCH ;ADD BAT LOGGING FLAGS AND PROCESSING ;DSK:DSKPAK.CDC;21 18-Jun-80 12:10:24, Edit by FRENCH ;REDEFINE DSKUER FOR NO CONFLICT WITH DSKUOP ;DSK:DSKPAK.CDC;20 16-Jun-80 11:41:09, Edit by FRENCH ;ON ERROR, UDSKIO GETS RETURNED STATUS BITS IN 1 ;SPLIT UDSKIO Q TABLES AND ADD RETURNED STATUS TABLE ;DSK:DSKPAK.CDC;8 12-Jun-80 17:50:26, Edit by FRENCH ;START SWAP ERROR RECOVERY LOGIC ;DSK:DSKPAK.CDC;5 12-Jun-80 15:01:54, Edit by FRENCH ;CHANGE RETRY COUNTER FROM 1000 TO 5, OFF AMPEXES NOW ;DSK:DSKPAK.CDC;2 12-Jun-80 12:17:29, Edit by FRENCH ;ADDED UDSKIO ERROR BIT DSKUER ;DSK:DSKPAK.CDC;32 27-May-80 11:25:30, Edit by FRENCH ;DON'T JUMP TO DERROR FROM WRONG PLACES, ADDED DSKERR ;DSKPAK.CDC;30 23-May-80 17:38:26 EDIT BY FRENCH ;TEST B6,9,10 FOR DETERMINATION OF BAD SPOT FOR NOW. ;REMOVE TEST OF BSPOTS IN EFANCY ;DSK:DSKPAK.CDC;23 14-May-80 16:55:27, Edit by FRENCH ;REDID DSKPAR ;DSK:DSKPAK.CDC;13 28-Apr-80 13:01:15, Edit by FRENCH ;ADDED AUTO BAT BLK QUEUER AND LOGGER ;DSK:DSKPAK.F3A;13 17-Apr-80 17:10:17, Edit by FRENCH ;RENAME TO DSKPAK.CDC ;DSK:DSKPAK.F3A;12 17-Apr-80 13:41:52, Edit by FRENCH ;ADDED FILIFG ;DSK:DSKPAK.F3A;11 15-Apr-80 17:17:40, Edit by FRENCH ;MADE NSECTK INTERNAL ;DSK:DSKPAK.F3A;6 11-Apr-80 14:31:15, Edit by FRENCH ;MAKE DREAD AND DWRITE INTERNAL ;DSK:DSKPAK.F3A;5 10-Apr-80 13:26:53, Edit by FRENCH ;REMOVED CDDSCA, CHANGED MY MIND ABOUT CRASH DUMP XB ADRSING ;DSK:DSKPAK.F3A;7 3-Apr-80 20:00:48, Edit by FRENCH ;MOVED CVDSK AND CDSKVA FROM HERE TO DSK.MAC ;DSK:DSKPAK.F3A;6 2-Apr-80 16:01:00, Edit by FRENCH ;MOVED NUNTBT,NCYLBT,NSRFBT,NSECBT TO PARAMS ;DSK:DSKPAK.F3A;4 28-Mar-80 15:22:49, Edit by FRENCH ;MOVE CVSWAD AND CVSWAD FROM HERE TO DSK.MAC ;DSK:DSKPAK.F3A;3 28-Mar-80 15:00:45, Edit by FRENCH ;ADDED CVADSW ROUTINE ;DSK:DSKPAK.F3A;1 26-Mar-80 17:01:44, Edit by FRENCH ;ADDED CDDSCA AND FDDSCA ;<134-TENEX>DSKPAK.F3A;10 26-Jan-80 22:14:41 EDIT BY PETERS ; Try one thousand times before DERROR, turn off compare flags ;<134-TENEX>DSKPAK.F3A;9 6-Jan-80 18:07:11 EDIT BY PETERS ; Moved NTKUN, NSURFS, NSECS, and NWSEC to PARAMS ;<134-TENEX>DSKPAK.F3A;8 2-Sep-79 20:40:51 EDIT BY PETERS ;<134-TENEX>DSKPAK.F3A;7 2-Sep-79 18:07:21 EDIT BY PETERS ;<134-TENEX>DSKPAK.F3A;6 2-Sep-79 18:02:54 EDIT BY PETERS ;<134-TENEX>DSKPAK.F3A;5 2-Sep-79 17:38:59 EDIT BY PETERS ;<134-TENEX>DSKPAK.F3A;4 2-Sep-79 16:56:25 EDIT BY PETERS ;<134-TENEX>DSKPAK.F3A;3 2-Sep-79 16:54:28 EDIT BY PETERS ;<134-TENEX>DSKPAK.F3A;2 30-Aug-79 19:02:56 EDIT BY PETERS ;<134-TENEX>DSKPAK.F3A;1 30-Aug-79 17:24:49 EDIT BY PETERS ;DSKPAK MODULE FOR F3A DISK DRIVES INTERN DSKINI,DSKRST,DSKCHK,DSKSV,DSKIO,UDSKIO,SWPTK,SWPTKH INTERN DIDSCI,DSKBSZ,NDSKPR,DSKPAR,CHKDSK,CHKDE1,NSECTK INTERN QUEBAT EXTERN DSKTIM,NXTDMP,FPTA,JFNOFN,SETPT,SETMPG,CPOPJ,SKPRET,DRMINI EXTERN PI5AC,PISC5R,DISE,DISL,EDISMS,DBUGSW,JB0FLG,NTRBAT DSTKSZ==20 ;SIZE OF DISK INTERRUPT STACK DSKUMX==4 ;MAXIMUM NUMBER OF QUEUED UDSKIO ITEMS CMPPG==377 ;MMAP SCRATCH PAGE FOR DISK DATA COMPARES CMPPGA==CMPPG*1000 ;FIRST ADDRESS IN ABOVE SAID PAGE OPDEF DKCO[740000,,0] ;DISK CONO IOT OPDEF DKCI[741000,,0] ;DISK CONI IOT OPDEF DKSO[742000,,0] ;DISK CONSO IOT OPDEF DKSZ[743000,,0] ;DISK CONSZ IOT ;BIT DEFINITIONS FOR F3A DISKS DSKIFG==000010 ;DISK INTERRUPT BIT IN RH DISK "CONI" WORD DSKUOP==200000 ;UDSKIO BIT IN LH DSKSTS DSKXFG==100000 ;MAIN XFER DONE BIT IN LH DSKSTS ;SIGNIFICANT ONLY IF DISK COMPARES ENABLED DSKUDN==100000 ;UDSKIO DONE BIT IN LH DSKUQ1 ENTRY DSKUER==40000 ;UDSKIO ERROR BIT IN LH DSKUQ1 ENTRY DSKULG==20000 ;UDSKIO LOG BAD SPOTS IN BAT BLOCKS DSKULA==10000 ;UDSKIO LOG EVEN ASSIGNED BAD SPOTS IN BATS ;MORE PARAMETERS FOR F3A DISKS NTRACK==NTKUN*NPACKS ;TOTAL CYLINDERS ON ALL PACKS COMBINED MNTRCK==-NTRACK ;BECAUSE OF MACRO... NSECPG==1000/NWSEC ;NUMBER OF SECTORS PER PAGE NSECTK==NSURFS*NSECS ;NUMBER OF SECTORS PER CYLINDER NPGTK==NSECTK*NWSEC/1000 ;NUMBER OF PAGES PER CYLINDER NBWTK==/^D36 ;NUMBER OF BIT TABLE BIT WORDS PER CYLINDER NMINFP==NPGTK/3 ;FREE CHOICE PARAMETER FOR DSKASN DSKNST==<<</NPGTK>+NPACKS-1>/NPACKS>*NPACKS ;TOTAL NUMBER OF SWAPPING CYLINDERS ;ON ALL PACKS COMBINED SWPTK==NTKUN/2-/2 ;FIRST SWAPPING CYLINDER ;ON EACH PACK SWPTKH==SWPTK+ ;ONE MORE THAN THE LAST SWAPPING ;CYLINDER ON EACH PACK SWPSEC==/NPACKS ;NUMBER OF SWAPPING SECTORS PER PACK LOTRK==0 ;FIRST CYLINDER ON ALL PACKS HITRK==NTRACK ;ONE MORE THAN THE LAST CYLINDER ON ALL PACKS DSKBSZ==<+777>/1000>*1000 ;TOTAL NUMBER OF WORDS IN ;DISK BIT TABLE ROUNDED UP ;TO THE NEXT FULL PAGE DSKBTB==DSKFCT+NTRACK ;FIRST WORD OF BITS IN DISK BIT TABLE DIDSC0==*NSECTK ;VERY FIRST DISK ADDRESS ;STORAGE DECLARATIONS LS DSKSRQ,NPACKS ;POINTER TO UNIT SWAP READ QUEUE LS DSKSWQ,NPACKS ;POINTER TO UNIT SWAP WRITE QUEUE LS DSKUWC,1 ;COUNT OF WAITING UDSKIO ITEMS LS DSKUQC,1 ;COUNT OF USED UDSKIO BLOCKS LS DSKUQ1,DSKUMX ;UDSKIO QUEUE STAUS WORD LS DSKUQ2,DSKUMX ;UDISKIO QUEUE DISK ADR WORD LS DSKUQ3,DSKUMX ;UDISKIO QUEUE RETURNED STATUS WORD LS DSKACP,1 ;SAVED AC P DURING DISK INTERRUPTS LS DSKSTK,DSTKSZ ;DISK INTERRUPT STACK LS DSKSVR,1 ;DISK INTERRUPT RETURN WORD LS DSKBLK,1 ;LOCK WORD FOR DISK BIT TABLE LS DBTJFN,1 ;JFN FOR DISK BIT TABLE FILE LS DIDSCA,1 ;VERY FIRST ASSIGNED FILE DISK ADDRESS ;ALSO XB ADR OF INDEX.;1 LS FDDSCA,1 ;XB ADR OF DIRECTORY.;1 LS FILIFG,1 ;FLAG TO SAY FILE SYS OK TO USE AFTER FILINI LS DSKLUN,1 ;UNIT NUMBER OF CURRENTLY ACTIVE DRIVE ;OR -1 IF NONE LS DSKCAW,NPACKS ;REAL CORE STARTING ADDRESS FOR CURRENT OR ;MOST RECENT DATA TRANSFER ON THIS DRIVE LS DSKDAW,NPACKS ;HARDWARE DISK ADDRESS FOR CURRENT OR MOST ;OPERATION ON THIS DRIVE LS DSKSTS,NPACKS ;SOFTWARE STATUS INFORMATION ABOUT THIS DRIVE LS DSKCTM,NPACKS ;OVERDUE TIMER FOR THIS DRIVE LS DSKTRY,NPACKS ;ERROR RETRY COUNTER FOR THIS DRIVE ;DISK ERROR DATA GTTAB STORAGE LS DSKRCE,1 ;TOTAL NUMBER OF RECOVERABLE DISK ERRORS LS DSKRER,4 ;DATA FROM MOST RECENT RECOVERABLE DISK ERROR LS DSKNRE,1 ;TOTAL NUMBER OF IRRECOVERABLE DISK ERRORS LS DSKLER,4 ;DATA FROM MOST RECENT IRRECOVERABLE DISK ERROR NDSKEW==^D10 ;TOTAL NUMBER OF WORDS IN THIS GTTAB TABLE NBATEN==^D10 ;LENGTH OF BAT QUEUE TABLE LS BATEOK,1 ;NONE 0 IF OK TO USE BATENQ TABLE LS BATENQ,NBATEN ;BAT BLK ENTRY QUEUE TABLE ;LITERAL STORAGE DECLARATIONS ;DSKPAR GTTAB TABLE - USED BY CHECKDISK, SWPBAT, BLKLUK, MAYBE OTHERS DSKPAR: LOTRK ;0 - FIRST CYLINDER ON STRUCTURE HITRK ;1 - LAST CYLINDER+1 ON STRUCTURE NSECTK ;2 - # SECTORS / CYLINDER NWSEC ;3 - # WORDS / SECTOR NTKUN ;4 - # CYLINDERS / PACK NPACKS ;5 - # PACKS ON STRUCTURE SWPTK ;6 - 0TH SWAPPING TRACK ON A PACK SWPTKH ;7 - LAST + 1 SWAPPING CYLINDER ON A PACK NSURFS ;10 - # SURFACES OR HEADS / PACK NSECS ;11 - # SECTORS / TRACK (NOT PER CYLINDER) NSECPG ;12 - # SECTORS / PAGE NBWTK ;13 - # BITTABLE WORDS / CYLINDER DSKBSZ ;14 - SIZE OF BITTABLE (COUNT + BIT AREAS) NMINFP ;15 - DSKASN FREE CHOICE PARAMETER ^-DSKMSK,,-1 ;16 - MASK OF BITS IN SOFTWARE ADR B8+B17+B26+B35 ;17 - 9 BIT HDWR SPEC POINT NUNTBT,0,UNTLSB ;20 - UNIT PART OF HDWR DISK ADR POINT NCYLBT,0,CYLLSB ;21 - CYL PART OF HDWR DISK ADR POINT NSRFBT,0,SRFLSB ;22 - SURFACE OR HEAD PART OF HDWR DISK ADR POINT NSECBT,0,SECLSB ;23 - SECTOR PART OF HDWR DISK ADR NDSKPR==.-DSKPAR ;NUMBER OF WORDS IN THIS GTTAB TABLE DIDSCI: DIDSC0+DSKABT ;VERY FIRST DISK ADDRESS ;INITIALIZATION AND RESTART CODE DSKINI: DSKRST: CALL DRMINI ;FIRST INIT PESUDO-DRUM STUFF DKCO 0 ;SHUT OFF DISK INTERRUPTS SETOM DSKLUN ;SAY NO OPERATIONS IN PROGRESS SETOM DSKBLK ;UNLOCK DISK BIT TABLE MOVSI A,-NPACKS ;PREPARE FOR PER-UNIT INITIALIZATION LOOP DSKRS1: CALL DIUNIT ;DO INITIALIZATION FOR THIS UNIT AOBJN A,DSKRS1 ;LOOP IF MORE UNITS TO INITIALIZE MOVSI 1,-NBATEN ;INIT BAT QUEUE TABLE DSKRS2: SETOM 1,BATENQ(1) ;-1 IS FREE (1B0 NOT PART OF HDWR ADR) AOBJN 1,DSKRS2 AOS BATEOK ;SAY TABLE HAS BEEN INITED RET DIUNIT: RET ;DO NOTHING HERE FOR NOW... ;PERIODIC CHECK OF DISK - CALLED HERE FROM SCHEDULER PROCESS LEVEL DSKCHK: MOVEI A,^D1000 ;DO THIS ONCE PER SECOND MOVEM A,DSKTIM ;SAVE FOR CLOCK ROUTINE TO USE MOVSI D,-NPACKS ;SET UP AOBJN LOOP TO CHECK DRIVES DSKCH1: SKIPN A,DSKCTM(D) ;OP PENDING ON THIS DRIVE? JRST DSKCH2 ;NO, LOOP TO NEXT DRIVE SUB A,TODCLK ;SEE IF OVERDUE JUMPGE A,DSKCH2 ;JUMP IF NOT OVER DUE YET TIMERR: SOSG DSKTRY(D) ;HAVE WE ALREADY LOST TOO MANY TIMES? BUG (HLT,) BUG(CHK,) PIOFF ;LOCK OUT WORLD DKCO DSKCHN ;SHUT OFF DISK CHANNEL AND CLEAR FLAG CALL RECAL ;FIRST RECALIBRATE MOVE A,DSKCAW(D) ;GET BACK CORE ADDRESS MOVE B,DSKDAW(D) ;AND DISK ADDRESS CALL DSKXFR ;GO INIT XFER PION ;TURN INTERRUPTS BACK ON DSKCH2: AOBJN D,DSKCH1 ;LOOP TO NEXT UNIT IF MORE RET CHKDSK: JFCL ;FALL THRU TO BAT STUFF ;CALLED BY JOB 0 TO MAKE ANY BAT ENTRIES NECESSARY DSKJB0: SKIPN BATEOK ;OK TO USE BATENQ? RET ;NO-NOT INITED YET MOVSI 1,-NBATEN ;NOW CHK BAT ENTRY Q TABLE DSKJB1: MOVE 2,BATENQ(1) ;GET IT CAME 2,[-1] ;FREE? CALL DSKBAT ;NO-DO IT SETOM 1,BATENQ(1) ;SAY ITS FREE NOW AOBJN 1,DSKJB1 ;LOOP FOR ALL OF TABLE RET ;HERE WITH BATENQ INDEX IN 1, THE ENTRY ITSELF IN 2 DSKBAT: PUSH P,1 ;SAVE AOBJN PTR PUSH P,2 ;SAVE ENTRY MOVE 1,2 ;GET HDWR DISK ADR & FLAG IN 1 CALL NTRBAT ;ENTER INTO BAT BLOCK JRST [ HRROI 1,[ASCIZ/ [Job 0 FAILED to make auto BAT block entry for (HDWR fmt): /] JRST DSKBA1] HRROI 1,[ASCIZ/ [Job 0 made auto BAT block entry for (HDWR fmt): /] DSKBA1: PSOUT MOVEI 1,101 MOVE 2,(P) TLZ 2,(1B0) ;CLEAR POSSIBLE FLAG MOVEI 3,10 NOUT JFCL HRROI 1,[ASCIZ/] /] PSOUT POP P,2 POP P,1 RET ;RET TO DOOP, FAKE READ OK CHKDE1: RET ;INTERRUPT SERVICE ROUTINE FOR F3A DISKS DSKSV: XWD DSKSVR,.+1 ;JSYS HERE FROM PISRV DKCI 0 ;COLLECT CONI STATUS TRNN 0,DSKIFG ;IS THIS INTERRUPT FOR US? JRST @DSKSVR ;NOTA... DKCO 0 ;CLEAR FLAG AND ZERO OUT CHANNEL SKIPGE D,DSKLUN ;WERE WE WAITING FOR SOMETHING TO HAPPEN? JRST @DSKSVR ;NOTA... MOVEM P,DSKACP ;SAVE AC P MOVE P,[IOWD DSTKSZ,DSKSTK] ;ESTABLISH CONTEXT RC 0 ;GET HARDWARE STATUS BITS TRNE 0,S.ANY ;ANY "NORMAL" ERROR OCCUR? ANYERR: JRST DSKSER ;YES, GO RETRY TLNE 0,S.IPE ;INTERNAL PARITY ERROR? IPEERR: JSP A,DSKDER ;YES, GO RETRY RM 0 ;GET ENDING MA PLUS ONE SUB 0,MA ;MINUS STARTING ADDRESS HRRZS 0 ;FLUSH GARBAGE BITS CAIE 0,1000 ;BETTER BE ONE THOUSAND EXACTLY... RMAERR: JSP A,DSKDER ;ELSE DATA XFER LOST... MOVSI A,DSKXFG ;SEE IF THIS IS A COMPARE XFER FINISHING TDNN A,DSKSTS(D) ;IS IT? JRST DSKSCC ;NO, GO SEE IF ONE IS NEEDED ANDCAM A,DSKSTS(D) ;YES, SHUT OFF COMPARE XFER DONE IN CASE LOSE CALL DSKCMP ;GO DO DATA COMPARE JRST DSKSE1 ;DATA COMPARE LOSSAGE, GO RE-DO EVERYTHING JRST DSKSVD ;ALL OK, GO MARK XFER COMPLETE DSKSCC: MOVSI A,DWRBIT ;PREPARE TO CHECK IF READ OR WRITE TDNE A,DSKSTS(D) ;SKIP IF READ JRST DSKSCW ;WRITE... SKIPN DSKRCF ;READ, ARE WE DOING READ COMPARES? JRST DSKSVD ;NO, GO MARK XFER COMPLETED DSKSCX: MOVSI A,DSKXFG ;YES IORM A,DSKSTS(D) ;SO MARK MAIN XFER COMPLETE MOVE A,DSKCAW(D) ;RESTORE MA MOVE B,DSKDAW(D) ;AND DA CALL DSKXFR ;AND GO INIT COMPARE XFER JRST DSKINR ;THEN DEBREAK DSKSCW: SKIPE DSKWCF ;WRITE, ARE WE DOING WRITE COMPARES? JRST DSKSCX ;YES, GO START IT UP DSKSVD: MOVE A,DSKSTS(D) ;GET STATUS FOR THAT UNIT SETZM DSKCTM(D) ;ZERO OUT OVERDUE TIMER TLNE A,DSKUOP ;WAS THIS A UDSKIO ENTRY? JRST DSKSVU ;YES, GO SET DONE BIT JSP D,SWPDON ;NO, NOTIFY SWAPPER JRST DSKSVS ;GO UNLOCK AND DO ANOTHER XFER IF NEEDED DSKSVU: MOVSS A ;GET UDSKIO BLOCK INDEX ANDI A,777 ;FLUSH NON-INDEX BITS MOVSI B,DSKUDN ;UDSKIO DONE BIT IORM B,DSKUQ1(A) ;TURN IT ON IN BLOCK MOVEM 0,DSKUQ3(A) ;AND SAVE RETURNED STATUS BITS SOS DSKUWC ;DECREMENT COUNT OF WAITING UDSKIO XFERS DSKSVS: SETOM DSKLUN ;SAY DISK NOW FREE FOR NEXT USE CALL DSKDOP ;THEN GO CHECK FOR ANY NEW STARTUPS NEEDED DSKINR: MOVE P,DSKACP ;RESTORE PREVIOUS CONTEXT MOVSI D,PI5AC ;AND BLT BACK LOW ACS BLT D,D ;SAVED BY PISRV JEN @PISC5R ;DEBREAK DSKCMP: MOVEI A,DSKWBF ;IF IT WAS A WRITE, WE DON'T HAVE TO MAP SETZ 0, ;CLEAR ERROR FLAG MOVSI C,-1000 ;SET UP COMPARE LOOP COUNT MOVSI B,DWRBIT ;CHECK WRITE BIT TDNE B,DSKSTS(D) ;WRITE? JRST DSKCLP ;YES, JUST GO DO COMPARE MOVE A,DSKCAW(D) ;READ, MUST DO MAP MADNESS LSH A,-^D9 ;MAKE CPN OUT OF MA PUSH P,CST0(A) ;SAVE PAGE STATE PUSH P,A ;SAVE CPN HRLI A,RWX!ACCESB ;MAKE MAP WORD OUT OF CPN MOVEM A,MMAP+CMPPG ;INSTALL IN MMAP MOVSI B,100000 ;PREPARE REASONABLE AGE MOVEM B,CST0(A) ;INSTALL IN PAGE STATE CONO PGR,1 ;CLEAR EXEC ARS MOVEI A,CMPPGA ;POINT COMPARER TO WRITE BUFFER DSKCLP: MOVE B,DSKRBF(C) ;GET A READ COMPARE WORD CAME B,0(A) ;BETTER BE THE SAME AS ORIGINAL DATA CMPERR: ADDI 0,1 ;ELSE ADD ONE TO ERROR COUNT ADDI A,1 ;INCREMENT WORD COUNT AOBJN C,DSKCLP ;LOOP IF MORE WORDS TO COMPARE MOVSI A,DWRBIT ;NOW CHECK IF READ OR WRITE TDNE A,DSKSTS(D) ;READ? JRST CMPRET ;NO, JUST CHECK STATE AND RETURN POP P,A ;YES, GET BACK CPN POP P,CST0(A) ;RESTORE PAGE STATE SETZM MMAP+CMPPG ;CLEAR GARBAGE MMAP WORD CONO PGR,1 ;CLEAR EXEC MAP CMPRET: CAIN 0,0 ;ANY ERRORS SEEN? AOS 0(P) ;NO, TAKE SKIP RETURN RET ;HERE ONLY FROM DSKSV DSKSER: SOSG DSKTRY(D) ;HAVE WE LOST TOO MANY TIMES? JRST EFANCY ;DO FANCY ERROR PROCESSING JRST DSKSE2 ;NO ;HERE FROM OTHER PLACES DSKSE1: SOSG DSKTRY(4) JSP A,DSKERR DSKSE2: TLNE 0,S.SKER ;SEEK ERROR? SEKERR: CALL RECAL ;YES, RECALIBRATE BEFORE RETRY MOVE A,DSKCAW(D) ;RE-DO MAIN XFER MOVE B,DSKDAW(D) ;RESTORE NEEDED DISK ADDRESS CALL DSKXFR ;GO INIT XFER JRST DSKINR ;AND GO BACK TO PISRV DSKDER: MOVSI B,DWRBIT ;PREPARE TO SEE IF READ OR WRITE TDNE B,DSKSTS(D) ;SKIP IF READ JRST DSKDEW ;WRITE SKIPE DSKRCF ;SKIP IF WE ARE NOT DOING READ COMPARES JRST 0(A) ;WE ARE, RETURN TO DO COMPARE JRST DSKSE1 ;WE AREN'T, GO TRY AGAIN DSKDEW: SKIPE DSKWCF ;SKIP IF WE ARE NOT DOING WRITE COMPARES JRST 0(A) ;WE ARE, RETURN TO DO COMPARE JRST DSKSE1 ;WE AREN'T, GO TRY AGAIN ;HERE VIA JSP A, DSKERR: BUG (HLT,) JRST DSKERR ;MUST $G AWAY ;FANCY DISK ERROR HANDLING AT PI LEVEL ;WE ONLY GET HERE VIA A JRST FROM DSKSER ;TO WIN IN THIS STUFF FOR BAD SPOTS: ; MUST BE READ OPERATION THAT FAILED ; MUST NOT BE DOING READ COMPARES ;STATUS BITS IN 0 ;DRIVE INDEX IN 4 EFANCY: PUSH P,1 ;CLOBBER NO ACS AT PI LEVEL PUSH P,2 PUSH P,3 MOVE 1,DSKSTS(4) TLNN 1,DWRBIT ;WRITE? (ASSUME NO BAD SPOT WRITES DETECTED) SKIPE DSKRCF ;OR DOING READ COMPARES? JRST EFANCX ;YES-DON'T GET TANGLED UP CALL EBADSP ;IS ERROR CLASSIFIED AS A BAD SPOT? JRST EFANCX ;NOT A BAD SPOT - DIE TLNN 1,DSKUOP ;UDSKIO READ? JRST ESWPIN ;NO-SWAP READ EUDSKI: LDB 1,[POINT ^D9,DSKSTS(4),^D17] ;GET DSKUQ_ INDEX FROM DSKSTS MOVSI 2,DSKUER ;PASS ERROR BIT IORB 2,DSKUQ1(1) ;INTO UDSKIO Q TLNN 2,DSKULG ;WANT TO LOG BAD SPOTS IN BATS? JRST ERETRN ;NO-DONE MOVE 1,DA ;THE DISK ADR OF CONCERN TLZ 1,(1B0) ;ASSUME NO BAT ENTRY FOR ASSIGNED DISK ADR TLNE 2,DSKULA ;BUT WANT THEM? TLO 1,(1B0) ;YES-TELL QUEBAT ABOUT IT CALL QUEBAT ;QUEUE FOR JOB 0 BAT BLK ENTRY JRST EFANCZ ;FAILED ERETRN: POP P,3 POP P,2 POP P,1 JRST DSKSVD ;FAKE OK READ, USER SHOULD SEE ERROR BITS ;HERE WHEN READ ERROR WAS OF SWAPPING TYPE. ESWPIN: MOVE 1,DSKCAW(4) ;GET RCA FOR LOSING TRANSFER LSH 1,-^D9 ;MAKE CPN MOVSI 2,SWPERR ;ERROR BIT IORM 2,CST3(1) ;INTO PLACE, ACTION WILL BE TAKEN IN SWPDON MOVE 1,DA ;THE DISK ADR OF CONCERN TLO 1,(1B0) ;SAY WANT BAT ENTRY EVEN FOR ASSIGNED ADR CALL QUEBAT ;QUEUE FOR JOB 0 BAT BLK ENTRY JRST EFANCZ ;FAILED JRST ERETRN ;OK, SWPDON WILL MARK XB AS PTING TO BAD SPOT ;OR FIX UP DRUM ADR ;CALL THIS TO DETERMINE IF DISK ERROR IS DUE TO BAD SPOT (ECC ERR FOR NOW) ;SKIPS FOR BAD SPOTS EBADSP: TLNE 0,(1B6!1B9!1B10) ;ECC ERRORS ARE BAD SPOTS FOR NOW AOS (P) RET ;CALLED AT DISK PI LEVEL OR BY PROCESS (DOES PIOFF FOR LOCKING OF QUEUE) ;CALL THIS TO QUEUE DISK ADR FOR ENTRY INTO BAT BLKS BY JOB 0 ;ACCEPTS 1/ DISK ADR ! 1B0 TO FORCE BAT ENTRY EVEN IF DSK ADR IS ASSIGNED ;SKIPS FOR SUCCESS. ;AN ENTRY IS XB0!DA WHERE X IS 1 TO FORCE BAT ENTRY EVEN FOR ASSIGNED ADRS QUEBAT: MOVSI 3,-NBATEN ;LOOK FOR ROOM PIOFF ;LOCK UP QUEBA1: MOVE 2,BATENQ(3) ;GET ENTRY CAMN 2,[-1] ;FREE? JRST QUEBA2 ;YES AOBJN 3,QUEBA1 ;LOOK AT EM ALL PION ;RESTORE PI RET ;NO ROOM QUEBA2: MOVEM 1,BATENQ(3) ;Q IT, DSKCHK WILL HANDLE IT PION ;RESTORE PI AOS JB0FLG ;GET JOB 0 TO DO THE WORK AOS (P) RET ;HERE WHEN SOMETHING NOT RIGHT. EFANCX: POP P,3 ;RESTORE ACS POP P,2 POP P,1 JSP A,DSKERR ;JUST AS DSKSER WOULD HAVE ;HERE WHEN QUEBAT FAILS ABOVE EFANCZ: BUG (CHK,) JRST EFANCX ;DISK I/O DRIVER, FIRST TYPE - CALLED FROM SWAPPER ONLY WITH ;CORE PAGE NUMBER IN AC A - B0 OFF=READ, B0 ON=WRITE DSKIO: PUSH P,A ;PRESERVE DWRBIT AND CPN AGAINST CLOBBERAGE MOVEI B,CST3(A) ;MAKE A POINTER TO THIS ENTRY MOVEI C,DSKSRQ ;POINT TO READ QUEUE TLNE A,DWRBIT ;UNLESS A WRITE IS REQUESTED MOVEI C,DSKSWQ ;THEN POINT TO WRITE QUEUE PIOFF ;MUST NOT INTERRUPT QUEUEING EXCH B,0(C) ;INSTALL POINTER TO NEW ENTRY HLL B,A ;INSTALL DWRBIT FOR THIS ENTRY MOVEM B,CST3(A) ;FINISH QUEUE SETUP PION ;OK TO INTERRUPT NOW SKIPGE DSKLUN ;ANYTHING HAPPENING NOW? CALL DSKDOP ;NO, GO TRY TO START IT UP POP P,A ;RETURN DWRBIT AND CPN TO CALLER RET ;DISK I/O DRIVER, SECOND TYPE - CALLED FOR ALL DISK ACCESS EXCEPT ;FROM WITHIN THE SWAPPER WITH HARDWARE DISK ADDRESS IN AC A, WORD COUNT ;AND B14 OFF=READ, B14 ON=WRITE IN AC B, REAL CORE ADDRESS IN AC C ;WORD COUNT IS IGNORED (ALWAYS 1000 OCTAL) AND REAL CORE ADDRESS IS FORCED ;TO BE AT THE START OF A PAGE BOUNDRY ;AC2/ 1B0 => LOG BAD SPOTSIN BAT BLOCKS, 1B1 => LOG EVEN ASSIGNED SPOTS IN BATS UDSKIO: PUSH P,A ;SAVE HARDWARE DISK ADDRESS UDSKI1: PIOFF ;LOCK OUT WORLD BEFORE QUEUEING ITEM MOVEI A,DSKUMX ;MAXIMUM ALLOWED DSKUQ_ ITEMS CAMLE A,DSKUQC ;ALREADY FULL? JRST UDSKI2 ;NO, GO QUEUE ITEM PION ;FULL, TURN INTERRUPTS BACK ON MOVEI A,DUQTST ;PREPARE TO WAIT FOR FREE ENTRY JSYS EDISMS ;SLEEP UNTIL SPACE AVAILABLE JRST UDSKI1 ;TRY, TRY, TRY AGAIN UDSKI2: MOVSI A,-DSKUMX ;PREPARE AOBJN TO FIND FREE BLOCK UDSKI3: SKIPN DSKUQ1(A) ;THIS BLOCK FREE? JRST UDSKI4 ;YES, GO SET IT UP AOBJN A,UDSKI3 ;GO CHECK NEXT BLOCK BUG(HLT,) JRST PINRET ;TURN ON INTERRUPTS AND RETURN UDSKI4: LSH C,-^D9 ;MAKE CPN OUT OF RCA HRLI C,DSKUOP(A) ;INSTALL UDSKIO BIT AND BLOCK INDEX TLNE B,10 ;WRITE? TLO C,DWRBIT ;YES TLNE B,(1B0) ;LOG BAD SPOTS IN BAT BLOCKS? TLO C,DSKULG ;YES TLNE B,(1B1) ;EVEN ASSIGNED IN FILESYSTEM BAD SPOTS? TLO C,DSKULA ;YES MOVEM C,DSKUQ1(A) ;PUT FIRST WORD IN BLOCK POP P,DSKUQ2(A) ;RESTORE HARDWARE DISK ADDRESS TO BLOCK AOS DSKUQC ;ONE MORE UDSKIO BLOCK USED AOS DSKUWC ;ONE MORE WAITING REQUEST PION ;OK TO INTERRUPT NOW PUSH P,A ;SAVE INDEX FOR ZEROING ENTRY SKIPGE DSKLUN ;DISK NOW FREE? CALL DSKDOP ;YES, GO START SOMETHING MOVE A,0(P) ;GET INDEX FOR WAIT TEST HRLI A,DXFTST ;PREPARE TO WAIT FOR ITEM TO BE DONE MOVSS A ;DATA,,WAIT-TEST FOR EDISMS JSYS EDISMS ;WAIT UNTIL UDSKIO DONE BIT SETS POP P,B ;RESTORE INDEX MOVE C,DSKUQ1(B) ;GET STATUS BITS FROM Q ENTRY SETZ A, ;INIT ERROR BITS TO NONE TLNE C,DSKUER ;ERROR BIT SET IN Q ENTRY? MOVE A,DSKUQ3(B) ;ERROR, RETURN SAVED STATUS BITS PIOFF ;SHUT OUT WORLD WHILE UNQUEUEING SETZM DSKUQ1(B) ;ZERO BITS AND CPN SETZM DSKUQ2(B) ;AND ALSO HARDWARE DISK ADDRESS SETZM DSKUQ3(B) ;AND RETURNED STATUS BITS SOS DSKUQC ;AND DECREMENT USED COUNT PINRET: PION ;TURN ON INTERRUPTS RET DUQTST: MOVEI A,DSKUMX ;MAXIMUM NUMBER OF UDSKIO BLOCKS CAMG A,DSKUQC ;STRICTLY GREATER THAN CURRENT COUNT? JRST 0(D) ;NO, KEEP WAITING JRST 1(D) ;YES, OK TO QUEUE NOW DXFTST: MOVSI B,DSKUDN ;UDSKIO DONE BIT TDNN B,DSKUQ1(A) ;XFER COMPLETED? JRST 0(D) ;NO, KEEP WAITING JRST 1(D) ;YES, RESTART PROCESS ;HERE WE SIMPLY MAKE VERY SURE THE DISK IS FREE AND THEN UNQUEUE ;AN OPERATION FOR STARTUP IF IT REALLY IS OR DO NOTHING IF IT ISN'T DSKDOP: PIOFF ;FREEZE WORLD BEFORE CHECKING THINGS SKIPL DSKLUN ;DISK WILL PROBABLY BE FREE HERE JRST PINRET ;BUT JUST RETURN IF NOT SKIPE DSKUWC ;UDSKIO REQUESTS WAITING? JRST DSKDO3 ;YES, DO THEM FIRST CALL UNQSRD ;NO, ANY SWAP READS TO DO? JRST DSKDO1 ;NO, GO CHECK FOR SWAP WRITES JRST DSKDO2 ;YES, GO DO IT DSKDO1: CALL UNQSWR ;NO, ANY SWAP WRITES WAITING? JRST PINRET ;NOTHING TO DO, JUST RETURN DSKDO2: PUSH P,A ;SAVE DWRBIT AND CPN MOVE A,CST1(A) ;GET BACKUP ADDRESS FOR THIS PAGE TLNN A,(DSKABT) ;REGULAR DISK ADDRESS? CALL CVSWAD ;NO, CONVERT SWAP ADDRESS CALL CVDSK ;NOW CONVERT TO HARDWARE ADDRESS, EITHER WAY MOVE B,A ;MOVE DISK ADDRESS TO B AS IN UDSKIO CASE POP P,A ;RESTORE DWRBIT AND CPN JRST DSKDO7 ;GO SET UP PHYSICAL XFER DSKDO3: MOVSI B,-DSKUMX ;PREPARE TO FIND A UDSKIO ITEM DSKDO4: SKIPN A,DSKUQ1(B) ;THIS BLOCK IN USE? JRST DSKDO5 ;NO, GO CHECK NEXT TLNN A,DSKXFG ;YES, IS XFER ALREADY COMPLETED? JRST DSKDO6 ;NO, GO START IT UP DSKDO5: AOBJN B,DSKDO4 ;GO CHECK NEXT BLOCK BUG(HLT,) JRST PINRET ;TURN ON PI AND RETURN DSKDO6: MOVE B,DSKUQ2(B) ;GET DISK ADDRESS AND FALL THROUGH DSKDO7: LDB D,[POINT NUNTBT,B,<^D35-NCYLBT-NSRFBT-NSECBT>] MOVEM D,DSKLUN ;INSTALL REAL UNIT IN DSKLUN MOVEM A,DSKSTS(D) ;INSTALL NEEDED INFO IN DSKSTS HRRZS A ;FLUSH STATUS BITS LSH A,^D9 ;NOW CONVERT CPN TO RCA MOVEM A,DSKCAW(D) ;SAVE AWAY CORE ADDRESS OF XFER PER DRIVE MOVEM B,DSKDAW(D) ;LAST DISK ADDRESS OF XFER PER DRIVE MOVEI C,^D1000 ;RETRY ONE THOUSAND TIMES BEFORE GIVING UP MOVEM C,DSKTRY(D) ;INIT RETRY COUNTER AS SUCH CALL DSKXFR ;GO INIT XFER JRST PINRET ;TURN ON INTERRUPTS AND RETURN ;HERE TO PROCESS A SWAP READ QUEUE ;CALL: QUEUE TO SEARCH IN AC A ;RETURN: +1 IF NO ITEM FOUND, +2 WITH CPN OF ITEM IN AC A UNQSRD: MOVEI A,DSKSRQ ;ONLY ONE QUEUE TO LOOK AT FOR NOW... SKIPN B,0(A) ;SKIP IF ANY ITEMS ON QUEUE, LOAD FIRST RET ;ITEM POINTER, ELSE SAY NONE FOUND UNQSRL: HRRZ C,0(B) ;GET RH OF ITEM TO AC C JUMPE C,UNQSRQ ;GO UNQUEUE IF END MOVE A,B ;ELSE MOVE POINTER TO POINTER-POINTER MOVE B,C ;AND ITEM TO POINTER JRST UNQSRL ;THEN GO CHECK NEXT ITEM UNQSRQ: HLLZS 0(A) ;ZERO POINTER, THUS UNQUEUEING ITEM SKIPGE 0(B) ;MAKE SURE ITEM IS A READ, ELSE DIE BUG(HLT,) MOVEI A,-CST3(B) ;RETURN CPN IN AC A AOS 0(P) ;TAKE SKIP RETURN RET ;HERE TO PROCESS A SWAP WRITE QUEUE ;CALL: QUEUE TO SEARCH IN AC A ;RETURN: +1 IF NO ITEM FOUND, +2 WITH DWRBIT,,CPN OF ITEM IN AC A UNQSWR: PUSH P,E ;SAVE AC E WHICH WE MAY CLOBBER MOVEI B,DSKSWQ ;ONLY ONE QUEUE TO LOOK AT FOR NOW... UNQSWL: HRRZ C,0(B) ;GET POINTER TO ITEM JUMPE C,UNQSWQ ;GO TRY TO UNQUEUE AN ITEM IF END MOVEI A,-CST3(C) ;MAKE ITEM CPN OUT OF POINTER MOVSI D,700000 ;PREPARE TO CHECK AGE FIELD TDNN D,CST0(A) ;HAS PAGE BEEN TOUCHED SINCE BEING QUEUED? JRST UNQSWD ;NO, UPDATE CONTEXT AND LOOP TO NEXT ITEM MOVSI D,(CORMB) ;YES, MAKE SURE CORE MODIFIED BIT IS SET IORM D,CST0(A) ;IN CST0 ENTRY OF ITEM BEFORE DELETING HRRZ D,0(C) ;PUT POINTER TO ITEM-NEXT HRRM D,0(B) ;IN POINTER TO ITEM-THIS, THUS UNQUEUING ITEM PUSH P,B ;SAVE POINTER-POINTER AGAINST SWPDON JSP D,SWPDON ;CLEAR IO REQUEST IN SWAPPER POP P,B ;RESTORE POINTER-POINTER JRST UNQSWL ;LOOP TO NEXT ITEM UNQSWD: MOVE E,B ;SAVE OLD POINTER-POINTER MOVE B,C ;OVERWRITE OLD POINTER-POINTER WITH NEW JRST UNQSWL ;LOOP TO NEXT ITEM UNQSWQ: MOVE A,E ;PRESERVE SAVED POINTER-POINTER IF ANY POP P,E ;RESTORE SAVED AC E CAIN B,DSKSWQ ;ANY ITEMS NOW IN QUEUE? RET ;NO, JUST RETURN HLLZS 0(A) ;YES, UNQUEUE SKIPL 0(B) ;MAKE SURE ITEM IS A WRITE, ELSE DIE BUG(HLT,) MOVEI A,-CST3(B) ;RETURN CPN IN AC A HRLI A,DWRBIT ;INSTALL DWRBIT AOS 0(P) ;TAKE SKIP RETURN RET ;HERE TO ACTUALLY START AN XFER - CALLED FROM PROCESS AND INTERRUPT ;LEVEL - A: DWRBIT,,CPN B: HDW DISK ADDR D: UNIT DSKXFR: MOVSI C,DWRBIT ;PREPARE TO CHECK FOR READ TDNE C,DSKSTS(D) ;IS IT? JRST DSKXWR ;NO, GO TO WRITE CODE SKIPE DSKRCF ;ARE WE DOING READ COMPARES? JRST DSKXRC ;YES, BRANCH TO DIFFERENT CODE CALL DREAD ;NO, JUST DO THE READ JRST DSKXRT ;NOW UPDATE TIMER AND RETURN DSKXRC: MOVSI C,DSKXFG ;PREPARE TO SEE IF MAIN XFER OR COMPARE READ TDNE C,DSKSTS(D) ;SKIP IF MAIN XFER NOT DONE YET DSKXRR: MOVEI A,DSKRBF ;COMPARE READ GOES TO READ COMPARE BUFFER CALL DREAD ;START IT UP JRST DSKXRT ;NOW RESET TIMER AND RETURN DSKXWR: SKIPE DSKWCF ;ARE WE DOING WRITE COMPARES? JRST DSKXWC ;YES, BRANCH TO DIFFERENT CODE CALL DWRITE ;NO, JUST START IT UP JRST DSKXRT ;NOW UPDATE TIMER AND RETURN DSKXWC: MOVSI C,DSKXFG ;PREPARE TO SEE IF MAIN XFER OR COMPARE READ TDNE C,DSKSTS(D) ;SKIP IF MAIN XFER NOT DONE YET JRST DSKXRR ;MAIN XFER DONE, GO DO COMPARE READ LSH A,-^D9 ;MAKE MA INTO CPN PUSH P,CST0(A) ;SAVE PAGE STATE MOVSI C,100000 ;INSTALL REASONABLE AGE MOVEM C,CST0(A) ;IN PAGE STATE WORD HRLI A,RWX!ACCESB ;MAKE A NICE PRIVATE PAGE POINTER MOVEM A,MMAP+CMPPG ;INSTALL IT IN MMAP CONO PGR,1 ;CLEAR EXEC ARS HERE MOVE C,[CMPPGA,,DSKWBF] ;PREPARE FOR BLT BLT C,DSKWBF+777 ;TO WRITE BUFFER SETZM MMAP+CMPPG ;CLEAR GARBAGE MMAP WORD POP P,CST0(A) ;RESTORE PAGE STATE CONO PGR,1 ;RE-CLEAR EXEC MAP MOVEI A,DSKWBF ;POINT TO WRITE BUFFER CALL DWRITE ;NOW GO START IT UP DSKXRT: MOVE A,TODCLK ;GET CURRENT TIME ADDI A,^D1000 ;GEVE IT ONE SECOND MOVEM A,DSKCTM(D) ;SAVE FOR OVERDUE CHECK DKCO DSKCHN ;MAKE SURE DISK HAS CHANNEL RET DSKRCF: 0 ;FLAG TO SAY DO READ COMPARES DSKWCF: 0 ;FLAG TO SAY DO WRITE COMPARES ;DEFINE REGISTERS PDL,A,B SOMEWHERE ;CALL: MOVE A,[] ; MOVE B,[BYTE(6)UNIT(13)CYLINDER(8)HEAD(8)SECTOR] ; PUSHJ PDL, ; ; ;CLOBBERS A,B,C INTERNAL DWRITE,DREAD ;DEFS. OF TEMP. IOTS OPDEF LA[723000,,0] OPDEF RA[717000,,0] OPDEF LC[721000,,0] OPDEF RC[715000,,0] OPDEF LE[724000,,0] OPDEF LM[722000,,0] OPDEF RM[716000,,0] LS DA,1 LS MA,1 LS CMD,1 LS RCNT,1 LS ERRBIT,1 LS DINTFG,1 ;RIGHT HALF ERROR BITS S.ANY==200 S.NACT==20 ;LEFT HALF BITS S.NBSY==1000 S.SERR==400000 S.IPE==10 ;INTERNAL PARITY ERROR S.OVRN==40 ;DATA OVERRUN S.WRP==200000 ;UNIT IS WRITE PROTECTED S.NRDY==100000 ;UNIT NOT READY S.SKER==20000 ;SEEK ERROR (CODE BELOW DOES AUTO. RECALIBRATE) S.FALT==10000 ;UNIT FAULT DREAD: TDZA C,C ;READ COMMAND IS ALL ZERO. DWRITE: MOVEI C,11000 ;WRITE COMMAND. PUSH P,0 ;0 USED AS TEMP. BELOW MOVEM B,DA ;SAVE DISK ADDRESS CALL DOOP ;DO THE OPERATION. POP P,0 RET DOOP: MOVEM A,MA ;RECORD THE DATA ADDRESS. MOVEM C,CMD ;ALSO THE COMMAND. MOVEI 10 ;RETRY COUNT MOVEM RCNT LE [2] ;RESET CTRL DOOP1: MOVE 1,DA ;GET DISK ADDRESS LA 1 ;DESELECT TLO 1,2000 ;INSTALL SELECT ENABLE BIT LA 1 ;SELECT DRIVE AND LOAD DSK ADR. RC 0 ;READ STATUS BITS. TLNE S.SERR ;SELECT ERROR ? B.SERR: JSP B,B.ERR MOVEI A,404000 ;GIVE CLEAR FAULT COMMAND. SETZM DINTFG ;TELL GO TO WAIT FOR COMPLETION CALL GO JSP B,B.ERR ;TIMEOUT RETURN MOVE A,CMD ;NOW GIVE MAIN COMMAND. LM MA ;LOAD THE MEMORY ADDRESS. RC 0 TLNN S.NRDY ;BETTER STILL BE READY... TRNE S.ANY ;...NO ERRORS SHOULD HAVE APPEARED... R.ANY: JSP B,B.ERR SETOM DINTFG ;TELL GO TO RETURN IMMEDIATELY CALL GO JRST [ CALL RECAL ;RECALIBRATE IF TIMEOUT JRST ERR1] ;THEN GO RETRY IF NOT TOO MANY ERRORS TLNN S.IPE ;INTERNAL PARITY ERROR ? TRNE S.ANY ;ANY OTHER ERROR ? G.ANY: JSP B,B.ERR DOOPX: RET GO: LE [2] ;INIT THE CONTROLLER. LC A ;SEND THE COMMAND. LE [1] ;START THE CONTROLLER. SKIPE DINTFG ;ARE WE TO WAIT FOR COMPLETION? JRST SKPRET ;NO, RETURN IMMEDIATELY MOVEI A,60000 ;TIMEOUT DELAY GOL: RC 0 IMULI 4,1 ;STOP USING MEMORY FOR A WHILE. TRNN S.NACT ;CHECK FOR ACTIVE SOJG A,GOL JUMPLE A,.+2 ;CHECK FOR TIMED OUT... AOS (P) ;SKIP RETURN IF NO ERROR... RET B.ERR: SUBI B,1 TLNE S.SERR ;SELECT ERROR ? JSP A,DERROR ;DIE ! TLNE S.SKER ;SEEK ERROR ? CALL RECAL ;YES, RECALIBRATE ERR1: SOSG RCNT ;HAVE WE RETRIED ENOUGH ? JSP A,DERROR ;YES. JRST DOOP1 ;TRY, TRY, TRY AGAIN... RECAL: MOVE A,[1404000] ;RECALIBRATE SETZM DINTFG ;TELL GO TO WAIT FOR COMPLETION CALL GO JSP A,DERROR ;DIE IF THIS TIMES OUT TRNE S.ANY JSP A,DERROR ;OR IF ANY ERROR OCCURED RET DERROR: BUG(HLT,) JRST DERROR END